package logger

import (
	"fmt"
	"gitee.com/zhucheer/orange/cfg"
	"gitee.com/zhucheer/orange/internal"
	"gitee.com/zhucheer/orange/logger/proto"
	pb "github.com/golang/protobuf/proto"
	"github.com/pierrec/lz4"
	cls "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cls/v20201016"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/regions"
	"time"
)

var clsLogger *ClsLogger

type ClsLogger struct {
	credential *common.Credential
	cpf        *profile.ClientProfile
	client     *cls.Client
	logId      string
	appName    string
}

type LogItem struct {
	Key   string
	Value string
}

type clsOption struct {
	appName   string
	appKey    string
	appSecret string
	logId     string
	region    string
	isDebug   bool
}

type clsLogChan struct {
	LogMsg  []byte
	LogFile string
	Record  *Record
}

// CLS日志通道
var clsChan chan clsLogChan

// 支持腾讯云cls日志提交
// 初始化腾讯云sdk对象
func initCloudHandler() {
	// 获取腾讯云基础信息
	option := getClsConfit()
	if option == nil || option.appKey == "" || option.appSecret == "" || option.logId == "" {
		return
	}
	if option.region == "" {
		option.region = regions.Guangzhou
	}
	clsLogger = &ClsLogger{
		logId:   option.logId,
		appName: option.appName,
	}

	if clsChan == nil {
		clsChan = make(chan clsLogChan, 20000)
	}
	clsLogger.credential = common.NewCredential(option.appKey, option.appSecret)
	clsLogger.cpf = profile.NewClientProfile()
	clsLogger.cpf.HttpProfile.ReqTimeout = 5
	//cpf.Debug = true

	var err error
	clsLogger.client, err = cls.NewClient(clsLogger.credential, option.region, clsLogger.cpf)
	if err != nil {
		internal.ConsoleLog(fmt.Sprintf("logger tencent cloud cls error"))
		return
	}

	syncClsLog(option.isDebug)
	internal.ConsoleLog(fmt.Sprintf("logger tencent cloud cls start region[%v] logId[%v]", option.region, option.logId))
	return
}

// 异步写入CLS
func syncClsLog(isDebug bool) {
	go func() {
		for {
			select {
			case logData := <-clsChan:
				sendClsStream(logData.Record)
				if isDebug {
					fmt.Println("cls chan len --->", len(clsChan))
				}
			}
		}
	}()
}

func getClsConfit() (option *clsOption) {
	if cfg.Config == nil {
		return
	}

	option = &clsOption{
		appKey:    cfg.GetString("app.logger.cls.secretId", ""),
		appSecret: cfg.GetString("app.logger.cls.secretKey", ""),
		logId:     cfg.GetString("app.logger.cls.logId", ""),
		region:    cfg.GetString("app.logger.cls.ap", ""),
		isDebug:   cfg.GetBool("app.logger.cls.debug", false),
		appName:   cfg.GetString("app.name", "orange"),
	}

	return
}

func sendClsChan(record *Record) {
	if clsLogger == nil || clsLogger.client == nil || clsChan == nil {
		return
	}

	clsChan <- clsLogChan{
		Record: record,
	}

	return
}

// 发送腾讯云cls日志
func sendClsStream(record *Record) (err error) {
	if clsLogger == nil || clsLogger.client == nil {
		return
	}
	logList := []LogItem{
		{"level", record.LevelString},
		{"message", record.Message},
		{"time", record.Time},
		{"funcName", record.FuncName},
		{"file", record.FileName},
		{"app", clsLogger.appName},
		{"line", fmt.Sprintf("%d", record.Line)},
	}
	for _, item := range record.KvField {
		logList = append(logList, LogItem{item.Key, fmt.Sprintf("%v", item.Value)})
	}

	// 创建common request
	headers := map[string]string{
		"X-CLS-TopicId": clsLogger.logId,
	}
	commpresstype := "" //压缩算法类型， 目前只支持lz4
	body := gnPbBody(logList...)
	length := lz4.CompressBlockBound(len(body)) + 1
	compressbody := make([]byte, length)
	n, err := lz4.CompressBlock(body, compressbody, nil)
	if err == nil && n > 0 {
		commpresstype = "lz4"
		body = compressbody[0:n]
	}
	headers["X-CLS-CompressType"] = commpresstype

	request := tchttp.NewCommonRequest("cls", "2020-10-16", "UploadLog")
	request.SetOctetStreamParameters(headers, body)
	//创建common response
	response := tchttp.NewCommonResponse()

	//发送请求
	err = clsLogger.client.SendOctetStream(request, response)
	return
}

// 日志数据转成pb数据
func gnPbBody(logList ...LogItem) []byte {
	nowtimestamp := time.Now().Unix()

	content := make([]*proto.Log_Content, 0)
	for _, item := range logList {
		kk := item.Key
		vv := item.Value
		content = append(content, &proto.Log_Content{
			Key:   &kk,
			Value: &vv,
		})
	}

	log := proto.Log{
		Time:     &nowtimestamp,
		Contents: content,
	}

	logGroup := proto.LogGroup{
		Logs: []*proto.Log{&log},
	}
	logGroupList := proto.LogGroupList{
		LogGroupList: []*proto.LogGroup{
			&logGroup,
		},
	}
	data, _ := pb.Marshal(&logGroupList)
	return data
}
